package dk.flashnotes.fff.page {	import br.com.stimuli.loading.BulkLoader;
	import br.com.stimuli.loading.BulkProgressEvent;
	
	import dk.flashnotes.fff.EMenuXMLKeys;
	import dk.flashnotes.fff.EPageXMLKeys;
	import dk.flashnotes.fff.FFFBase;
	import dk.flashnotes.fff.page.EPageType;
	import dk.flashnotes.fff.page.part.APart;
	import dk.flashnotes.fff.page.part.PartControl;
	import dk.flashnotes.fff.page.part.PartControlEvent;
	import dk.flashnotes.fff.stateHolder.StateHolder;
	import dk.flashnotes.fff.stateHolder.StateHolderEvent;
	import dk.flashnotes.fff.tree.TreeNode;
	
	import org.osflash.thunderbolt.Logger;
	
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;		
	/**	 * @author josef	 */	public class APage extends Sprite {
		private var _oTN : TreeNode;
		// elements		private var _oSH : StateHolder;		private var _oURLLoader : URLLoader;		private var _oPageData : PageData;		private var _oPartControl : PartControl;
		private var _oAssetLoaderMain : BulkLoader;		private var _oPageSettings : PageSettings;
		private var _sprPartContainer : Sprite;		private var _oParentPage : APage;		private var _oFFFBase : FFFBase;		private var _sprBlock : Sprite;		private var _bCloseFromOverlay : Boolean = false;		private var _bMarkedForKill : Boolean = false;
		
		public function APage(oTN : TreeNode) {									_oFFFBase = FFFBase.get_oInstance();							_oTN = oTN;			_oTN.registerPage(this);						_oPageSettings = new PageSettings();						super();					}				final public function initPage():void {						createElements();						// init parts			_oPartControl = new PartControl(this);						onCreatePartsDirect();						if (_oTN.oTNData.sDataUrl != "") {				loadData();			} else if (_oTN.oTNData.sDataUrl == "" && _oTN.oTNData.sDataRef != "") {				retriveDataFromBaseXML();			} else {				registerPartsAndLoadAsset();			}		}
		private function createElements() : void {			setupPartContainer();			setupStateHolder();			setupAssetLoaderMain();			setupEventHandlers();		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// part containger		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function setupPartContainer() : void {			_sprPartContainer = new Sprite();			addChild(_sprPartContainer);		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Page event handling		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function setupEventHandlers() : void {			addEventListener(PageEvent.etPARTS_CREATED, onPartsCreated, false, 0, true);			addEventListener(PageEvent.etPAGE_CLOSED, onPageClosed, false, 0, true);		}
		private function onPartsCreated(e : Event) : void {		}
		private function onPageClosed(e : Event) : void {			onPageClear();			clearPage();		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Page clearing		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		protected function onPageClear() : void {		}
		private function clearPage() : void {						addEventListener(StateHolderEvent.etSTATE_CHANGED, onStateChanged, false, 0, true);						_bMarkedForKill = true;			_oSH.sState = EPageStates.CLEARED;		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Main assetLoader		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function setupAssetLoaderMain() : void {			_oAssetLoaderMain = new BulkLoader(sUniqePageId);		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// State holder		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function setupStateHolder() : void {						_oSH = new StateHolder(EPageStates.CREATING, this);						_oSH.registerStateOutEventDispatch(EPageStates.LOADING_DATA, new PageEvent(PageEvent.etDATA_LOADED, this));			_oSH.registerStateOutEventDispatch(EPageStates.CREATING_PARTS, new PageEvent(PageEvent.etPARTS_CREATED, this));						_oSH.registerStateInEventDispatch(EPageStates.READY_TO_OPEN, new PageEvent(PageEvent.etPAGE_READY_TO_OPEN, this));						_oSH.registerStateInEventDispatch(EPageStates.OPENING, new PageEvent(PageEvent.etSTART_OPEN, this));			_oSH.registerStateInEventDispatch(EPageStates.OPENED, new PageEvent(PageEvent.etPAGE_OPNED, this));						_oSH.registerStateInEventDispatch(EPageStates.CLOSING, new PageEvent(PageEvent.etSTART_CLOSE, this));			_oSH.registerStateInEventDispatch(EPageStates.CLOSED, new PageEvent(PageEvent.etPAGE_CLOSED, this));						_oSH.registerStateInEventDispatch(EPageStates.CLEARED, new PageEvent(PageEvent.etPAGE_CLEARED, this));		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Load data		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function loadData() : void {			_oURLLoader = new URLLoader();			_oURLLoader.addEventListener(Event.COMPLETE, onDataLoadComplete, false, 0, true);			_oURLLoader.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);			_oURLLoader.load(new URLRequest(_oTN.oTNData.sDataUrl));		}
		private function onIOError(e : IOErrorEvent) : void {			Logger.error("Data XML not found : " + _oTN.oTNData.sDataUrl);		}
		private function onDataLoadComplete(e : Event) : void {						_oURLLoader.removeEventListener(Event.COMPLETE, onDataLoadComplete);			_oURLLoader.removeEventListener(IOErrorEvent.IO_ERROR, onIOError);						_oPageData = new PageData(XML(XML(_oURLLoader.data)[EMenuXMLKeys.PAGE]));			createPartsFromXML();		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// retrive data		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function retriveDataFromBaseXML() : void {			_oPageData = _oFFFBase.get_oPageDataByRefrenceId(_oTN.oTNData.sDataRef);			createPartsFromXML();		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// Components		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function createPartsFromXML() : void {			_oSH.sState = EPageStates.CREATING_PARTS;						if (_oPageData) {				_oPartControl.createPartsFromPartDatas(XML(oPageData.xmlData[EPageXMLKeys.PARTS]));			}						//events			registerPartsAndLoadAsset();		}
		/**		 * Override in concrete APage to add Parts directly using the addPartDirect method.		 * 		 * This method insures that directly added parts will be added at the right time in the page creation process. Therefor all directly adde parts should be added within the implementation of this method.		 */		public function onCreatePartsDirect() : void {		}
		private function registerPartsAndLoadAsset() : void {						_oPartControl.addEventListener(PartControlEvent.etALL_PARTS_OPNED, onAllPartsOpned, false, 0, true);			_oPartControl.addEventListener(PartControlEvent.etALL_PARTS_CLOSED, onAllPartsClosed, false, 0, true);						registerParts();			loadAssets();		}
		private function registerParts() : void {			for each (var oPart : APart in _oPartControl.o_oParts) {				registerPart(oPart);			}		}
		
		/**		 * Adds a Part to the page. Use when you are instanciation directly from the Page and not through automatic instanciation through the page data xml. Overr		 * 		 * @param oPart:APart The Part you want to add to the page directly.		 * @see #onCreatePartsDirect()		 */		public function addPartDirect(oPart : APart) : void {			_oPartControl.addPartDirect(oPart);		}
		private function registerPart(oPart : APart) : void {						// this will add loaditems from parts			oPart.oPage = this;		}
		/*******************		 * asset loading		 ********************/
		private function loadAssets() : void {			_oSH.sState = EPageStates.LOADING_ASSETS;			if (_oAssetLoaderMain._itemsTotal != 0) {				_oAssetLoaderMain.addEventListener(BulkProgressEvent.COMPLETE, onMainAssetLoadComplete, false, 0, true);				_oAssetLoaderMain.start();			} else {				setPageReady();			}		}
		private function onMainAssetLoadComplete(e : BulkProgressEvent) : void {			_oAssetLoaderMain.removeEventListener(BulkProgressEvent.COMPLETE, onMainAssetLoadComplete);			setPageReady();		}
		/*******************		 * set page ready		 ********************/
		private function setPageReady() : void {			_oSH.sState = EPageStates.READY_TO_OPEN;		}
		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Open page		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::				/**		 * @private		 */		final public function openPage() : void {						if (_oSH.sState == EPageStates.READY_TO_OPEN) {				preparePageOpen();			} else {				addEventListener(PageEvent.etPAGE_READY_TO_OPEN, onPageReadyToOpen, false, 0, true);			}		}
		private function onPageReadyToOpen(e : Event) : void {			removeEventListener(PageEvent.etPAGE_READY_TO_OPEN, onPageReadyToOpen);			preparePageOpen();		}
		private function preparePageOpen() : void {						switch (_oPageSettings.sPageType) {				case EPageType.DEFAULT:					doOpenPage();					break;								case EPageType.NESTED:					setupNestedOpening();					break;								case EPageType.OVERLAY:					doOpenPage();					break;			}		}
		private function setupNestedOpening() : void {			//Logger.info("setting up nested opening based on : " + _oTN.oParentNode.oPage.oSH.sState)
			_oParentPage = _oTN.oParentNode.oPage;						switch(_oPageSettings.sWaitForParentOnOpen) {				case EPageSettings.NO_WAIT_FOR_PARENT:					_oParentPage.openPage();					doOpenPage();					break;				case EPageSettings.PARENT_OPEN:					if (_oParentPage.sState == EPageStates.OPENED) {						doOpenPage();					} else {						_oParentPage.addEventListener(PageEvent.etPAGE_OPNED, doOpenPage, false, 0, true);						_oParentPage.openPage();					}					break;				case EPageSettings.PARENT_READY:					if (_oParentPage.sState == EPageStates.OPENED) {						doOpenPage();					} else if (_oParentPage.sState == EPageStates.READY_TO_OPEN) {						doOpenPage();						_oParentPage.openPage();					} else {						_oParentPage.addEventListener(PageEvent.etPAGE_READY_TO_OPEN, doOpenPage, false, 0, true);						_oParentPage.openPage();					}			}		}
		
		private function doOpenPage(e : Event = null) : void {						if (FFFBase.get_oInstance().bTracingEnabled) {				Logger.info("OPENING : " + _oTN.oTNData.sTitle + " :: " + _oPageSettings.sPageType);			}						// check if the page is overlay type			if (_oPageSettings.sPageType == EPageType.OVERLAY) {				//setupOverlayMouseBlock();			}						_oSH.sState = EPageStates.OPENING;			_oPartControl.openParts();		}
		
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Close page		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::				/**		 * @private		 */		final public function closePage() : void {			preparePageClose();		}
		private function preparePageClose() : void {			var oOpenNestedPage : APage = _oTN.get_oOpenNestedPage();						if (oOpenNestedPage) {				setupNestedClosing(oOpenNestedPage);			} else {				doClosePage();			}		}
		private function doClosePage(e : Event = null) : void {						if (FFFBase.get_oInstance().bTracingEnabled) {				Logger.info("CLOSING : " + _oTN.oTNData.sTitle);			}						_oSH.sState = EPageStates.CLOSING;			_oPartControl.closeParts();		}
		private function setupNestedClosing(oOpenNestedPage : APage) : void {			switch(_oPageSettings.sWaitForNestedOnClose) {				case EPageSettings.NESTED_CLOSED:					oOpenNestedPage.addEventListener(PageEvent.etPAGE_CLOSED, doClosePage, false, 0, true);					oOpenNestedPage.closePage();					break;				case EPageSettings.NO_WAIT_FOR_NESTED:					oOpenNestedPage.addEventListener(PageEvent.etSTART_CLOSE, doClosePage, false, 0, true);					oOpenNestedPage.closePage();					break;			}		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// PartControl handlers		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function onAllPartsClosed(e : Event) : void {			IEventDispatcher(e.target).removeEventListener(e.type, arguments["callee"]);			_oSH.sState = EPageStates.CLOSED;		}
		private function onAllPartsOpned(e : Event) : void {			IEventDispatcher(e.target).removeEventListener(e.type, arguments["callee"]);			_oSH.sState = EPageStates.OPENED;		}
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// kill page		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::				/**		 * Kills the page and makes it ready for GB. 		 */
		private function onStateChanged(event : StateHolderEvent) : void {			if (_bMarkedForKill && sState == EPageStates.CLEARED) {				IEventDispatcher(event.target).removeEventListener(event.type, arguments["callee"]);				kill();			}		}
		private function kill() : void {						_oFFFBase = null;						//_oTN = null;			_oAssetLoaderMain.clear();			_oAssetLoaderMain = null;						_oPageData = null;			_oPageSettings = null;						_oURLLoader = null;						killParts();						_oPartControl = null;						while (numChildren>0) {				removeChildAt(0);			}						_sprPartContainer = null;			_oSH.kill();			_oSH.addEventListener(StateHolderEvent.etKILLED, onStateHolderKilled, false, 0, true);		}
		private function onStateHolderKilled(event : StateHolderEvent) : void {			IEventDispatcher(event.target).removeEventListener(event.type, arguments["callee"]);			_oSH = null;		}				final public function clearTNRefrence():void {			_oTN=null;		}		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::		// Kill Parts		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		private function killParts() : void {			_oPartControl.killParts();		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// getters		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::						
		public function get_a_oAPartsByPartType(cPartType : Class) : Array {			return _oPartControl.get_a_oApartsByType(cPartType);		}

		public function get_oApartByPartType(cPartType : Class) : APart {			return _oPartControl.get_a_oApartsByType(cPartType)[0];		}
		
		/**		 * Returns the name of the Page template type		 */		public function get sPageTemplateType() : String { 			return this.toString(); 		}		 
		/**		 * Returns the loaded data object of the Page		 */		public function get oPageData() : PageData {			return _oPageData;		}
		/**		 * A uniqe id gennerated as this : "page_" + _oTN.oTNData.sUrl		 */		public function get sUniqePageId() : String { 			return "page_" + _oTN.oTNData.sUrl;		}
		/**		 * The main asset loader. Allows Parts to add assets to a centralized loader		 */		public function get oAssetLoaderMain() : BulkLoader {			return _oAssetLoaderMain;		}
		/**		 * The TreeNode object of the Page.		 * @return TreeNode		 * @see dk.flashnotes.fff.tree.TreeNode		 */		public function get oTN() : TreeNode {			return _oTN;		}
		/**		 * The PageSettings of the Page		 */		public function get oPageSettings() : PageSettings {			return _oPageSettings;		}
		/**		 * The DisplayObjectContainer where the Parts will be placed.		 */		public function get sprPartContainer() : Sprite {			return _sprPartContainer;		}
		public function get a_doParts() : Array { 			return _oPartControl.a_doParts; 		}
		public function get bCloseFromOverlay() : Boolean {			return _bCloseFromOverlay;		}
		public function get sState() : String { 			return _oSH.sState; 		}
		public function set sState(oNewValue : String) : void { 			_oSH.sState = oNewValue;		}					}}